package pl.wroc.uni.ii.evolution.distribution.tasks;

import pl.wroc.uni.ii.evolution.distribution.clustering.EvSolutionSpaceLoader;
import pl.wroc.uni.ii.evolution.distribution.strategies.EvIslandModel;
import pl.wroc.uni.ii.evolution.engine.prototype.EvTask;

/**
 * Encapsulates all information about a given evolutionary task,
 * that means: algorithm, objective function, all necessary parameters.
 * 
 * @author Marcin Brodziak, Tomasz Kozakiewicz, Marcin Golebiowski
 */
public class EvIsland extends EvTask {

  private EvIslandModel strategy = null;
  private EvSolutionSpaceLoader loader = null;
  private Thread loader_thread = null;
  

  /**
   * Sets strategy for exchanging individuals
   * @param strategy
   */
  public void setDistributedStrategy(EvIslandModel strategy) {
    this.strategy = strategy;
  }
  
  /**
   * Set the way the task  loads new SolutionSpace
   * @param loader
   */
  public void setSolutionSpaceLoader(EvSolutionSpaceLoader loader) {
    this.loader = loader;
  }

  /**
   * Methods that runs algorithm basing on termination condition.
   */
  @Override
  @SuppressWarnings({ "unchecked", "deprecation" })
  public void run() {
   
    if(algorithm == null) {
      throw new IllegalStateException(
      "Run method of EvolutionaryTask required to call setAlgorithm(EvolutionaryAlgorithm) first.");
    }



    // init algorithm
    algorithm.init();

    // init distribution strategy
    if (strategy != null) {
      strategy.init();
    }
    
    // starts loader
    if (loader != null) {
      loader_thread = loader.start();
    }

    while (!algorithm.isTerminationConditionSatisfied()) {

      // check if thread is interrupted
      if (Thread.interrupted()) {
        break;
      }

      // do one algorithm iteration
      algorithm.doIteration();

      if (strategy != null) {

        if (!strategy.isOkey()) {
          System.out.println("Problem with strategy");
          break;
        }

        // synchronize with neighbours
        strategy.export(algorithm.getPopulation());
        strategy.updatePopulation(algorithm.getPopulation());
      }
      
      if (loader != null && loader.newSubspaceAvailable()) {  
        // load solution space from somewhere
        algorithm.setSolutionSpace(loader.takeSubspace());

        // init algorithm
        algorithm.init();

        // stops current strategy
        if (strategy != null) {
          strategy.stop();
        }

        // init new distribution strategy
        if (strategy != null) {
          strategy.init();
        }
      }
    }

    // stopping strategy
    if (strategy != null) {
      strategy.stop();
    }
    
    // stopping loader
    if (loader != null) {
      loader_thread.stop();
    }
  }
}
